﻿using PLCS.Domain.Stations;
using PLCS.Domain.Trays;
using PLCS.EntityFrameworkCore.EntityFrameworkCore;
using System;

namespace PLCS.EntityFrameworkCore.Stations
{
    public class StationRepository : EfCoreRepository<PLCSDbContext, Station, Guid>, IStationRepository
    {
        private static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        public StationRepository(IDbContextProvider<PLCSDbContext> dbContextProvider) : base(dbContextProvider)
        {
        }

        public override Task<Station> UpdateAsync(Station entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            return base.UpdateAsync(entity, autoSave, cancellationToken);
        }

        /// <summary>
        /// 通过工位编号获取工位
        /// </summary>
        /// <param name="staName"></param>
        /// <param name="lever">0为下级，1为staNum，2为上级</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<Station> GetStationByNumAsync(string staName, int lever = 1)
        {
            Station station = null;
            switch (lever)
            {
                case 0:
                    station = await FindAsync(x => x.StaName_Lower == staName);
                    break;

                case 1:
                    station = await FindAsync(x => x.StaName == staName);
                    break;

                case 2:
                    station = await FindAsync(x => x.StaName_Higher == staName);
                    break;

                default:
                    break;
            }
            return SynchronousTrays(station);
        }

        public override async Task<Station> FindAsync(Expression<Func<Station, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            var quable = await WithDetailsAsync(x => x.Trays);
            quable = quable.Where(predicate);
            return await AsyncExecuter.FirstOrDefaultAsync(quable);
        }

        /// <summary>
        /// 获取所有工位，按创建时间排序
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<IEnumerable<Station>> GetStationListAsync()
        {
            var queryable = await WithDetailsAsync(x => x.Trays);
            queryable = queryable.OrderBy(x => x.StaNum);
            var res = await queryable.AsNoTracking().ToListAsync();
            return SynchronousTrays(res);
        }

        /// <summary>
        /// 通过条件获取所有工位，按创建时间排序
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<IEnumerable<Station>> GetStationListAsync(Expression<Func<Station, bool>> predicate)
        {
            var queryable = await WithDetailsAsync(x => x.Trays);
            queryable = queryable.Where(predicate).OrderByDescending(x => x.CreationTime);
            var res = await queryable.AsNoTracking().ToListAsync();
            return SynchronousTrays(res);
        }

        private IEnumerable<Station> SynchronousTrays(List<Station> stations)
        {
            foreach (var station in stations)
            {
                if (station.StationType == Domain.Shared.Stations.StationType.栈工位)
                {
                    if (station.StackTrays == null)
                    {
                        station.StackTrays = new Stack<Tray>();
                    }
                    var sortedTrays = station.Trays?.OrderBy(t => t.EnterTime);
                    if (sortedTrays != null)
                        foreach (var tray in sortedTrays)
                        {
                            station.StackTrays.Push(tray);
                        }
                }
                else if (station.StationType == Domain.Shared.Stations.StationType.队列工位)
                {
                    if (station.QueueTrays == null)
                    {
                        station.QueueTrays = new Queue<Tray>();
                    }
                    var sortedTrays = station.Trays?.OrderBy(t => t.EnterTime);
                    if (sortedTrays != null)
                        foreach (var tray in sortedTrays)
                        {
                            station.QueueTrays.Enqueue(tray);
                        }
                }
            }
            return stations;
        }

        private Station SynchronousTrays(Station station)
        {
            if (station == null)
                return null;
            if (station.StationType == Domain.Shared.Stations.StationType.栈工位)
            {
                if (station.StackTrays == null)
                {
                    station.StackTrays = new Stack<Tray>();
                }
                var sortedTrays = station.Trays?.OrderBy(t => t.EnterTime);
                if (sortedTrays != null)
                    foreach (var tray in sortedTrays)
                    {
                        station.StackTrays.Push(tray);
                    }
            }
            else if (station.StationType == Domain.Shared.Stations.StationType.队列工位)
            {
                if (station.QueueTrays == null)
                {
                    station.QueueTrays = new Queue<Tray>();
                }
                var sortedTrays = station.Trays?.OrderBy(t => t.EnterTime);
                if (sortedTrays != null)
                    foreach (var tray in sortedTrays)
                    {
                        station.QueueTrays.Enqueue(tray);
                    }
            }
            return station;
        }

        public async Task DeleteAllAsync()
        {
            var dbContext = await GetDbContextAsync();
            await dbContext.Database.ExecuteSqlRawAsync("delete from T_Stations");
        }

        public async Task<IEnumerable<Station>> GetStationsByKindAsync(string kind)
        {
            var queryable = await WithDetailsAsync(x => x.Trays);
            queryable = queryable.Where(x => x.Kind == kind).OrderBy(x => x.StaNum);
            var res = await queryable.AsNoTracking().ToListAsync();
            return SynchronousTrays(res);
        }

        public async Task<IEnumerable<Station>> GetIntermediatePositions(int minPostion, int maxPostion)
        {
            var queryable = await GetQueryableAsync();
            queryable = queryable.Where(x => x.Position >= minPostion && x.Position <= maxPostion);
            var res = await queryable.ToListAsync();
            return res;
        }
    }
}